<template>
  <div class="graph-cont">
    <div ref="graph" class="graph dragging-to">
      <!--   data节点    -->
      <div
        v-for="(node, index) in dataNodes"
        :key="'data' + index"
        class="node"
        :class="{
          selected: selectedPipelineNode
            ? selectedPipelineNode.id === node.id
            : false,
        }"
        :style="{
          left: `${node.data.position.x}px`,
          top: `${node.data.position.y}px`,
          width: `${nodeWidth}px`,
          height: `${nodeHeight}px`,
          'line-height': `${nodeHeight}px`,
          'z-index': menuOpenedId === node.id ? 31 : 30,
        }"
        @mousedown="startDragging($event, node)"
        @click="selectNode($event, node)"
        @mouseleave="hideAllMenu"
      >
        <!-- 'z-index': menuOpenedId === node.id? 31: 30 节点层级 -->
        <!-- 禁用灰度 -->
        <div
          class="node-no-forbidden"
          :class="{ 'node-forbidden': node.data.forbidden }"
        ></div>
        <!-- icon -->
        <div class="type-icon">
          <a-icon-font class="type-icon-color" type="iconxitongshujubeifen3" />
        </div>
        <!-- name -->
        <!--     todo 节点名称 tooltip -->
        <div class="name">
          <!--          <a-tooltip placement="top">-->
          <!--            <template slot="title">-->
          <!--              <span>{{node.name}}</span>-->
          <!--            </template>-->
          <!--            <div class="node-name-tooltip">-->
          <!--              {{node.name}}-->
          <!--            </div>-->
          <!--          </a-tooltip>-->
          {{ node.name }}
        </div>
        <!-- ... menu hover  -->
        <div
          class="menu-hover-ellipsis"
          @mouseenter="nodeContextMenu($event, node)"
        >
          <a-icon-font class="type-icon-color" type="icongengduocaozuo" />
        </div>
        <!--  运行状态图标(根据查询运行节点状态显示) -->
        <div
          v-if="getNodeStatus(node.id).length > 0 && !comparisonStatus"
          class="status-icon"
        >
          <a-icon-font :type="getNodeStatus(node.id)" />
        </div>
        <!-- 可视化对比 -->
        <div
          v-if="comparisonStatus"
          class="comparison-select-status"
          @click="changeNodeComparison(node, $event)"
        >
          <a-icon-font
            v-if="comparisonNodeIds.includes(node.id)"
            type="iconxuanzeanniu2"
          />
          <a-icon-font v-else type="iconxuanzeanniu1" />
        </div>
        <!-- 下连接点 -->
        <div
          class="connect-cell connect-cell-down"
          @mousedown="
            startConnect($event, node.type, node.id, node.data.output)
          "
        ></div>

        <!--    menu    -->
        <ul
          v-if="menuOpenedId === node.id"
          class="menu"
          :class="{
            'menu-top': nodeMenuPosition(node.data.position),
            'menu-bottom': !nodeMenuPosition(node.data.position),
          }"
          @mouseleave="hideAllMenu"
          @mouseenter="nodeContextMenu($event, node)"
          @click="($event) => $event.stopPropagation()"
        >
          <li
            class="item"
            :class="{ 'not-useable': NodeStatus.length === 0 }"
            @click="checkDataInfo(node, $event)"
          >
            预览数据
          </li>
          <li class="divider"></li>
          <li class="item" @click="nodeCopy(node.id, $event)">复制</li>
          <li class="divider"></li>
          <li class="item" @click="forbiddenNode($event)">
            <span v-if="!node.data.forbidden">禁用</span>
            <span v-else>取消禁用</span>
          </li>
          <li class="item" @click="deleteNode(node.id, $event)">删除</li>
        </ul>
      </div>

      <!-- 算子 节点 -->
      <div
        v-for="(node, index) in operatorNodes"
        :key="'operator' + index"
        class="node"
        :class="{
          selected: selectedPipelineNode
            ? selectedPipelineNode.id === node.id
            : false,
        }"
        :style="{
          left: `${node.data.position.x}px`,
          top: `${node.data.position.y}px`,
          width: `${nodeWidth}px`,
          height: `${nodeHeight}px`,
          'line-height': `${nodeHeight}px`,
          'z-index': menuOpenedId === node.id ? 31 : 30,
        }"
        @mousedown="startDragging($event, node)"
        @click="selectNode($event, node)"
        @mouseleave="hideAllMenu"
      >
        <!-- 禁用灰度 -->
        <div
          class="node-no-forbidden"
          :class="{ 'node-forbidden': node.data.forbidden }"
        ></div>
        <!-- icon -->
        <div class="type-icon">
          <a-icon-font
            class="type-icon-color"
            :type="operatorIcon(node.data.algType)"
          />
        </div>
        <!-- name -->
        <div class="name">{{ node.name }}</div>
        <!-- ... menu hover  -->
        <div
          class="menu-hover-ellipsis"
          @mouseenter="nodeContextMenu($event, node)"
        >
          <a-icon-font class="type-icon-color" type="icongengduocaozuo" />
        </div>
        <!--  运行状态图标(根据查询运行节点状态显示) -->
        <div
          v-if="getNodeStatus(node.id).length > 0 && !comparisonStatus"
          class="status-icon"
        >
          <a-icon-font :type="getNodeStatus(node.id)" />
        </div>
        <!-- 可视化对比 -->
        <div
          v-if="comparisonStatus"
          class="comparison-select-status"
          @click="changeNodeComparison(node, $event)"
        >
          <a-icon-font
            v-if="comparisonNodeIds.includes(node.id)"
            type="iconxuanzeanniu2"
          />
          <a-icon-font v-else type="iconxuanzeanniu1" />
        </div>
        <!-- 上连接点 -->
        <div
          class="connect-cell connect-cell-up"
          @mouseup="endConnect(node.type, node.id, node, node.data.algType)"
        ></div>
        <!-- 下连接点 -->
        <div
          class="connect-cell connect-cell-down"
          @mousedown="
            startConnect($event, node.type, node.id, node.data.output)
          "
        ></div>
        <!--    menu    -->
        <ul
          v-if="menuOpenedId === node.id"
          class="menu"
          :class="{
            'menu-top': nodeMenuPosition(node.data.position),
            'menu-bottom': !nodeMenuPosition(node.data.position),
          }"
          @mouseleave="hideAllMenu"
          @mouseenter="nodeContextMenu($event, node)"
          @click="($event) => $event.stopPropagation()"
        >
          <li class="item" @click="editOperatorParams(node, $event)">
            参数配置
          </li>
          <li
            class="item"
            :class="{ 'not-useable': NodeStatus.length === 0 }"
            @click="nodePreview(node, $event)"
          >
            预览数据
          </li>
          <li class="divider"></li>
          <li class="item" @click="addVisual(node, $event)">添加可视化</li>
          <li class="divider"></li>
          <li class="item" @click="nodeCopy(node.id, $event)">复制</li>
          <li class="divider"></li>
          <li class="item" @click="forbiddenNode($event)">
            <span v-if="!node.data.forbidden">禁用</span>
            <span v-else>取消禁用</span>
          </li>
          <li class="item" @click="deleteNode(node.id, $event)">删除</li>
        </ul>
      </div>

      <!-- 自定义算子 节点 -->
      <div
        v-for="(node, index) in userDefinedNodes"
        :key="'user-operator' + index"
        class="node"
        :class="{
          selected: selectedPipelineNode
            ? selectedPipelineNode.id === node.id
            : false,
        }"
        :style="{
          left: `${node.data.position.x}px`,
          top: `${node.data.position.y}px`,
          width: `${nodeWidth}px`,
          height: `${nodeHeight}px`,
          'line-height': `${nodeHeight}px`,
          'z-index': menuOpenedId === node.id ? 31 : 30,
        }"
        @mousedown="startDragging($event, node)"
        @click="selectNode($event, node)"
        @mouseleave="hideAllMenu"
      >
        <!-- 禁用灰度 -->
        <div
          class="node-no-forbidden"
          :class="{ 'node-forbidden': node.data.forbidden }"
        ></div>
        <!-- icon -->
        <div class="type-icon">
          <a-icon-font class="type-icon-color" type="iconzidingyi" />
        </div>
        <!-- name -->
        <div class="name">{{ node.name }}</div>
        <!-- ... menu hover  -->
        <div
          class="menu-hover-ellipsis"
          @mouseenter="nodeContextMenu($event, node)"
        >
          <a-icon-font class="type-icon-color" type="icongengduocaozuo" />
        </div>
        <!--  运行状态图标(根据查询运行节点状态显示) -->
        <div
          v-if="getNodeStatus(node.id).length > 0 && !comparisonStatus"
          class="status-icon"
        >
          <a-icon-font :type="getNodeStatus(node.id)" />
        </div>
        <!-- 可视化对比 -->
        <div
          v-if="comparisonStatus"
          class="comparison-select-status"
          @click="changeNodeComparison(node, $event)"
        >
          <a-icon-font
            v-if="comparisonNodeIds.includes(node.id)"
            type="iconxuanzeanniu2"
          />
          <a-icon-font v-else type="iconxuanzeanniu1" />
        </div>
        <!-- 上连接点 -->
        <div
          class="connect-cell connect-cell-up"
          @mouseup="endConnect(node.type, node.id, node, node.data.algType)"
        ></div>
        <!-- 下连接点 -->
        <div
          class="connect-cell connect-cell-down"
          @mousedown="
            startConnect($event, node.type, node.id, node.data.output)
          "
        ></div>
        <!--    menu    -->
        <ul
          v-if="menuOpenedId === node.id"
          class="menu"
          :class="{
            'menu-top': nodeMenuPosition(node.data.position),
            'menu-bottom': !nodeMenuPosition(node.data.position),
          }"
          @mouseleave="hideAllMenu"
          @mouseenter="nodeContextMenu($event, node)"
          @click="($event) => $event.stopPropagation()"
        >
          <li class="item" @click="editOperatorParams(node, $event)">
            参数配置
          </li>
          <li
            class="item"
            :class="{ 'not-useable': NodeStatus.length === 0 }"
            @click="nodePreview(node, $event)"
          >
            预览数据
          </li>
          <li class="divider"></li>
          <li class="item" @click="addVisual(node, $event)">添加可视化</li>
          <li class="divider"></li>
          <li class="item" @click="nodeCopy(node.id, $event)">复制</li>
          <li class="divider"></li>
          <li class="item" @click="forbiddenNode($event)">
            <span v-if="!node.data.forbidden">禁用</span>
            <span v-else>取消禁用</span>
          </li>
          <li class="item" @click="deleteNode(node.id, $event)">删除</li>
        </ul>
      </div>

      <!-- ETL 节点 -->
      <div
        v-for="(node, index) in etlNodes"
        :key="'etl' + index"
        class="node"
        :class="{
          selected: selectedPipelineNode
            ? selectedPipelineNode.id === node.id
            : false,
        }"
        :style="{
          left: `${node.data.position.x}px`,
          top: `${node.data.position.y}px`,
          width: `${nodeWidth}px`,
          height: `${nodeHeight}px`,
          'line-height': `${nodeHeight}px`,
          'z-index': menuOpenedId === node.id ? 31 : 30,
        }"
        @mousedown="startDragging($event, node)"
        @click="selectNode($event, node)"
        @mouseleave="hideAllMenu"
      >
        <!-- 禁用灰度 -->
        <div
          class="node-no-forbidden"
          :class="{ 'node-forbidden': node.data.forbidden }"
        ></div>
        <!-- icon -->
        <div class="type-icon">
          <a-icon-font class="type-icon-color" type="iconETL" />
        </div>
        <!-- name -->
        <div class="name">{{ node.name }}</div>
        <!-- ... menu hover  -->
        <div
          class="menu-hover-ellipsis"
          @mouseenter="nodeContextMenu($event, node)"
        >
          <a-icon-font class="type-icon-color" type="icongengduocaozuo" />
        </div>
        <!--  运行状态图标(根据查询运行节点状态显示) -->
        <div
          v-if="getNodeStatus(node.id).length > 0 && !comparisonStatus"
          class="status-icon"
        >
          <a-icon-font :type="getNodeStatus(node.id)" />
        </div>
        <!-- 可视化对比 -->
        <div
          v-if="comparisonStatus"
          class="comparison-select-status"
          @click="changeNodeComparison(node, $event)"
        >
          <a-icon-font
            v-if="comparisonNodeIds.includes(node.id)"
            type="iconxuanzeanniu2"
          />
          <a-icon-font v-else type="iconxuanzeanniu1" />
        </div>
        <!-- 上连接点 -->
        <div
          class="connect-cell connect-cell-up"
          @mouseup="endConnect(node.type, node.id, node, node.data.algType)"
        ></div>
        <!-- 下连接点 -->
        <div
          class="connect-cell connect-cell-down"
          @mousedown="
            startConnect($event, node.type, node.id, node.data.output)
          "
        ></div>
        <!--    menu    -->
        <ul
          v-if="menuOpenedId === node.id"
          class="menu"
          :class="{
            'menu-top': nodeMenuPosition(node.data.position),
            'menu-bottom': !nodeMenuPosition(node.data.position),
          }"
          @mouseleave="hideAllMenu"
          @mouseenter="nodeContextMenu($event, node)"
          @click="($event) => $event.stopPropagation()"
        >
          <li class="item" @click="editEtlParams(node, $event)">参数配置</li>
          <li
            class="item"
            :class="{ 'not-useable': NodeStatus.length === 0 }"
            @click="nodePreview(node, $event)"
          >
            预览数据
          </li>
          <li class="divider"></li>
          <li class="item" @click="addVisual(node, $event)">添加可视化</li>
          <li class="divider"></li>
          <li class="item" @click="nodeCopy(node.id, $event)">复制</li>
          <li class="divider"></li>
          <li class="item" @click="forbiddenNode($event)">
            <span v-if="!node.data.forbidden">禁用</span>
            <span v-else>取消禁用</span>
          </li>
          <li class="item" @click="deleteNode(node.id, $event)">删除</li>
        </ul>
      </div>

      <!--   连接线   -->
      <div
        v-for="edge in getEdgeInfo()"
        :key="edge.key"
        class="flow-edge-cell"
        :style="{
          left: edge.x - 4 + 'px',
          top: edge.y - 10 + 'px',
        }"
      >
        <div
          class="edge-container"
          :style="{
            height: containerHeight(edge),
            width: containerWidth(edge),
          }"
        >
          <svg
            class="edge-svg"
            :style="{
              height: containerHeight(edge),
              width: containerWidth(edge),
            }"
          >
            <defs>
              <marker
                id="arrowhead"
                markerWidth="10"
                markerHeight="7"
                refX="9"
                refY="3.5"
                orient="auto"
              >
                <polygon points="0 0, 10 3.5, 0 7" fill="#9B9DA8" />
              </marker>
            </defs>
            <path
              :d="getPathD(edge)"
              class="line-cls"
              marker-end="url(#arrowhead)"
              :stroke-dasharray="lineStatus(edge.endNodeId)"
              :style="{
                'stroke-width':
                  strokeWidth +
                  (edge.startNodeId === startNodeId &&
                  edge.endNodeId === endNodeId
                    ? 0.6
                    : 0) +
                  'px',
              }"
              @mouseenter="hoverLine(edge)"
              @mouseleave="leaveLine()"
              @click="selectLine($event, edge)"
              @contextmenu="connectLineContextmenu($event, edge)"
            />
          </svg>
        </div>
      </div>

      <!--   连接线编辑:delete-fun="dropEdge"   -->
      <div
        v-if="edgeMenuInfo.show"
        :style="{
          position: 'absolute',
          left: edgeMenuInfo.x + 'px',
          top: edgeMenuInfo.y + 'px',
          'z-index': 1000,
          width: '124px',
        }"
        class="menu-container"
      >
        <a-menu class="a-menu-container">
          <a-menu-item class="a-menu-cell" @click="deleteConnectLine()"
            >删除</a-menu-item
          >
        </a-menu>
      </div>

      <!-- connecting line -->
      <div v-show="connectInfo.show" class="connect-line-div">
        <svg class="connect-line-svg">
          <path
            :d="`M${connectInfo.startEndPos[0]},${connectInfo.startEndPos[1]} L${connectInfo.startEndPos[2]},${connectInfo.startEndPos[3]}`"
            class="connect-line"
          ></path>
        </svg>
      </div>
    </div>

    <!-- 算子 etl 节点预览 -->
    <node-data-preview
      :current-info="[currentDataInfo]"
      :info-visible="infoDataVisible"
      @close-preview="infoDataVisible = false"
    />

    <!-- 数据节点 -->
    <node-data-preview
      :current-info="currentOperatorInfo"
      :info-visible="infoOperatorVisible"
      @close-preview="infoOperatorVisible = false"
    />

    <!-- popup -->
    <div v-if="visiblePopup.name" class="overlay">
      <!-- 算子 参数编辑 -->
      <set-parameters
        v-if="visiblePopup && visiblePopup.name === 'set-params'"
        class="window"
        :params="visiblePopup"
        :current-node="editCurrentNodeDeepCopy"
        @confirm="confirmOperatorParams"
        @done="cancelBtn"
      />
      <!-- ETL 行列过滤  -->
      <etl-ranks-filter
        v-if="visiblePopup && visiblePopup.name === 'etl-ranks-filter'"
        class="window"
        :params="visiblePopup"
        :current-node="editCurrentNodeDeepCopy"
        @confirm="confirmEtlParams"
        @done="cancelBtn"
      />
      <!-- ETL 采样  -->
      <etl-sample
        v-if="visiblePopup && visiblePopup.name === 'etl-sampling'"
        class="window"
        :params="visiblePopup"
        :current-node="editCurrentNodeDeepCopy"
        @confirm="confirmEtlParams"
        @done="cancelBtn"
      />
      <!-- ETL 执行SQL  -->
      <etl-sql
        v-if="visiblePopup && visiblePopup.name === 'etl-sql'"
        class="window"
        :params="visiblePopup"
        :current-node="editCurrentNodeDeepCopy"
        @confirm="confirmEtlParams"
        @done="cancelBtn"
      />
      <!-- ETL 关联 -->
      <etl-union
        v-if="visiblePopup && visiblePopup.name === 'etl-union'"
        class="window"
        :params="visiblePopup"
        :current-node="editCurrentNodeDeepCopy"
        @confirm="confirmEtlParams"
        @done="cancelBtn"
      />
      <!-- ETL 拼接 -->
      <etl-join
        v-if="visiblePopup && visiblePopup.name === 'etl-join'"
        class="window"
        :params="visiblePopup"
        :current-node="editCurrentNodeDeepCopy"
        @confirm="confirmEtlParams"
        @done="cancelBtn"
      />
    </div>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import { Prop, Watch } from 'vue-property-decorator'
import SetParameters from '@/components/studio/data/popup/SetParameters.vue'
import EtlRanksFilter from '@/components/studio/data/popup/EtlRanksFilter.vue'
import EtlSample from '@/components/studio/data/popup/EtlSample.vue'
import DataPreviewTable from '@/components/studio/data/panel/DataPreviewTable.vue'
import {
  allNodeList,
  addNodeToPipeline,
  deleteNodeById,
  updateNodeToPipeline,
  infoData,
  pipelineInstanceStatus,
  deleteLineById,
  nodeCopy,
  nodeDataPreview,
} from '@/api/task'
import EtlSql from '@/components/studio/data/popup/EtlSql.vue'
import EtlUnion from '@/components/studio/data/popup/EtlUnion.vue'
import EtlJoin from '@/components/studio/data/popup/EtlJoin.vue'
import VisualWindow from '@/components/studio/data/drawer/VisualWindow.vue'
import DataViewStore from '@/store/modules/dataview'
import {
  AlgTypeEnum,
  ETLAlgType,
} from '@/components/studio/data/panel/operator-emnu'
// 节点数据预览
import NodeDataPreview from '@/components/studio/data/panel/NodeDataPreview.vue'

interface Position {
  x: number
  y: number
}

@Component({
  components: {
    EtlJoin,
    EtlUnion,
    EtlSql,
    SetParameters,
    EtlRanksFilter,
    EtlSample,
    DataPreviewTable,
    VisualWindow,
    NodeDataPreview,
  },
})
export default class PipelineGraph extends Vue {
  @Prop({
    type: Number,
    default: 0,
  })
  pipelineId!: number
  @Prop({
    type: Boolean,
    default: false,
  })
  logStatus!: boolean
  @Prop({
    type: Number,
    default: 0,
  })
  projectId!: number
  @Prop({
    type: Boolean,
    default: false,
  })
  drawerStatus!: boolean
  @Prop({
    type: Boolean,
    default: false,
  })
  comparisonStatus!: boolean

  // 节点尺寸
  public nodeHeight = 32
  public nodeWidth = 178
  // popup name
  public visiblePopup = { name: '' }
  // 连接动作线 和 节点对信息
  public connectInfo = {
    show: false,
    startEndPos: [0, 0, 0, 0],
    pair: [
      { type: 0, nodeId: 0, output: [] },
      { type: 0, nodeId: 0, output: [] },
    ],
  }
  public dragging = false
  public mouseLastPosition = { x: 0, y: 0 }
  // 选择的节点
  public selectedPipelineNode: any | null = null
  // 节点编辑菜单
  public menuOpenedId: number | null = null
  // nodes of data
  public dataNodes: Array<any> = []
  // nodes of operator
  public operatorNodes: Array<any> = []
  // nodes of ETL
  public etlNodes: Array<any> = []
  // 用户自定义算子
  public userDefinedNodes: Array<any> = []
  // 当前编辑的节点（右键菜单， 编辑算子ETL参数, 拖拽位置, 连线结束存储）
  public editCurrentNode: any = {}
  // 深拷贝 整个节点信息（供给参数配置）
  public editCurrentNodeDeepCopy: any = {}
  // 连接线相关
  public pad = 10
  public strokeWidth = 1
  // 画布尺寸 位置 宽高
  public containerPos: Array<number> = []
  public containerHeightWidth: Array<number> = []
  // 轮询
  public myInterval: any

  public edgeMenuInfo: any = {
    show: false,
    x: 0,
    y: 0,
    targetInfo: { connect: [{}, {}] },
  }

  // 预览数据信息
  public currentDataInfo: any = {}
  public infoDataVisible: boolean = false

  // 算子etl 节点数据预览
  public currentOperatorInfo: Array<any> = []
  public infoOperatorVisible: boolean = false

  // 点击事件和拖拽事件冲突问题
  public firstTime: number = 0
  // 运行状态  节点status map
  public runStatusMap: any | null = null
  // 节点运行状态映射
  public NodeStatus: any = {
    CREATE: '',
    RUNNING: '',
    SUCCESS: 'iconyouquanxianlianjiechenggong',
    FAIL: 'iconwuquanxian',
    STOP: 'iconjinggaodanchuang',
    KILLED: 'iconwuquanxian',
  }
  // 参与可视化对比的节点id
  public comparisonNodes: Array<any> = []
  public comparisonNodeIds: Array<number> = []

  // hover click 连接线
  public startNodeId: number | null = null
  public endNodeId: number | null = null

  public mounted() {
    // 监听点击事件 关闭 所有节点menu 清空store中的 selectNode
    ;(document.querySelector(
      '.graph-content'
    ) as HTMLDivElement).addEventListener('click', this.cancelSelectNode)
    document.addEventListener('click', this.hideAllMenu)
    document.addEventListener('keyup', this.globalKeyUp)
    // 监听浏览器窗口变化
    window.addEventListener('resize', this.onResize)
    this.getAllNodes()
    this.initVariable()
  }

  public beforeDestroy() {
    document.removeEventListener('click', this.hideAllMenu)
    document.removeEventListener('keyup', this.globalKeyUp)
    window.removeEventListener('resize', this.onResize)
    clearInterval(this.myInterval)
  }

  public globalKeyUp($event: KeyboardEvent) {
    if ($event.key === 'Escape') {
      this.cancelBtn()
    }
  }

  /**
   * 监听浏览器窗口变化
   */
  public onResize() {
    this.initVariable()
  }

  /**
   * 取消选择的节点
   */
  public cancelSelectNode() {
    // 取消选择 节点
    this.selectedPipelineNode = null
    // 清空 store
    DataViewStore.changeSelectNode(null)
  }

  /**
   * 隐藏所有菜单 (连接线,节点)
   */
  public hideAllMenu() {
    // 关闭节点 menu
    this.menuOpenedId = null
    // 关闭连接线 menu
    this.edgeMenuInfo.show = false
  }

  @Watch('pipelineId')
  public changePipelineId() {
    // 暂停
    clearInterval(this.myInterval)
    this.getAllNodes()
    this.runStatusMap = null
  }
  @Watch('logStatus')
  public changeLogStatus() {
    this.initVariable()
  }
  @Watch('drawerStatus')
  public changeDrawerStatus() {
    setTimeout(() => this.initVariable(), 200)
  }
  @Watch('comparisonStatus')
  public changeComparisonStatus() {
    // 对比状态变化  清空对比列表
    this.comparisonNodeIds = []
    this.comparisonNodes = []
    // todo 添加可视化的节点不参与对比， 无法选择
    if (this.comparisonStatus) {
      // console.log('开始对比')
    }
  }

  /**
   * 清空画布节点  数据节点  算子节点  ETL节点
   */
  public clearNodes() {
    this.dataNodes = []
    this.operatorNodes = []
    this.etlNodes = []
    this.userDefinedNodes = []
  }

  /**
   * 初始化画布 位置 尺寸  containerPos  containerHeightWidth
   */
  public initVariable() {
    try {
      const containerRect = (this.$refs
        .graph as HTMLDivElement).getBoundingClientRect()
      this.containerPos = [containerRect.x, containerRect.y]
      this.containerHeightWidth = [containerRect.height, containerRect.width]
    } catch (error) {
      console.log(error)
    }
  }

  /**
   * DataView 父组件 获取 参与对比的 节点Id
   */
  public getComparisonNodes() {
    return this.comparisonNodes
  }

  /**
   * 取消选中
   */
  public cancelNodePitch() {
    this.comparisonNodeIds = []
    this.comparisonNodes = []
  }

  /**
   * 根据 projectId 和 pipelineId 获取当前project所有节点
   */
  public async getAllNodes() {
    if (this.pipelineId != null) {
      const parameters = {
        id: this.projectId,
        pipelineId: this.pipelineId,
      }
      console.log('********************', this.pipelineId)
      const response = await allNodeList({ data: parameters })
      if (response.data.code === 100) {
        this.clearNodes()
        // 节点分类渲染
        response.data.result.forEach((node: any) => {
          if (node.type === 1) {
            this.operatorNodes.push(node)
          } else if (node.type === 2) {
            this.etlNodes.push(node)
          } else if (node.type === 3) {
            this.dataNodes.push(node)
          } else if (node.type === 4) {
            this.userDefinedNodes.push(node)
          }
        })
      }
    } else {
      this.clearNodes()
    }
  }

  /**
   * 算子图标
   */
  public operatorIcon(algType: number) {
    return AlgTypeEnum[algType]
  }

  /**
   *  添加 数据节点 当前 pipeline
   *  @param data 添加的数据
   */
  public async addData(data: any) {
    if (this.pipelineId == null) {
      this.$message.warning('请先创建 pipeline 后再添加节点!')
      return
    }
    const parameters = {
      name: data.dropped.name,
      projectId: this.projectId,
      pipelineId: this.pipelineId,
      type: 3, // 类型: 3--表示数据节点， 1--算子， 2--ETL算子
      data: {
        dataset_id: data.dropped.id,
        position: data.position,
      },
    }
    const response = await addNodeToPipeline({ data: parameters })
    if (response.data.code === 100) {
      this.dataNodes.push(response.data.result)
      this.$message.success('添加数据成功！', 0.5)
    }
  }

  /**
   *  添加 算子 Etl节点 当前 pipeline
   *  @param operator 添加的 算子  ETL
   */
  public async addOperator(operator: any) {
    if (this.pipelineId == null) {
      this.$message.warning('请先创建 pipeline 后再添加节点!')
      return
    }
    const parameters = {
      name: operator.dropped.name,
      projectId: this.projectId,
      pipelineId: this.pipelineId,
      type: operator.dropped.type,
      data: {
        algName: operator.dropped.name,
        algType: operator.dropped.algType,
        position: operator.position,
      },
    }
    const response = await addNodeToPipeline({ data: parameters })
    // etl  算子分类处理
    if (response.data.code === 100) {
      if (response.data.result.type === 1) {
        this.operatorNodes.push(response.data.result)
        this.$message.success('添加算法成功！', 0.5)
      } else if (response.data.result.type === 2) {
        this.etlNodes.push(response.data.result)
        this.$message.success('添加ETL成功！', 0.5)
      } else if (response.data.result.type === 4) {
        this.userDefinedNodes.push(response.data.result)
        this.$message.success('添加自定义算子成功！', 0.5)
      }
    }
  }

  /**
   * 轮询拿 pipeline 节点运行状态
   * @param sessionId 执行的sessionId
   */
  public runStatusPoll(sessionId: number) {
    // 先请求一次状态
    this.getRunStatus(sessionId)
    setTimeout(() => {
      this.myInterval = setInterval(() => {
        this.getRunStatus(sessionId)
      }, 1000)
    }, 500)
  }

  /**
   * pipeline 节点运行状态 轮询接口
   * @param sessionId 执行的sessionId
   */
  public async getRunStatus(sessionId: number) {
    const parameters = {
      pipelineId: this.pipelineId,
      sessionId,
    }
    const response = await pipelineInstanceStatus({ data: parameters })
    if (response.data.code === 100) {
      this.runStatusMap = response.data.result.taskMap
      const { status } = response.data.result
      if (status === 'SUCCESS' || status === 'FAIL') {
        // 运行成功/失败  清除定时器 结束轮询
        clearInterval(this.myInterval)
      }
      this.$emit('running-log', {
        status,
        totoalLogs: response.data.result.totoalLogs,
      })
    }
  }

  /**
   * 节点运行状态 icon
   */
  public getNodeStatus(nodeId: number) {
    let status = ''
    if (
      this.runStatusMap != null &&
      Object.keys(this.runStatusMap).includes(`${nodeId}`)
    ) {
      status = this.NodeStatus[this.runStatusMap[`${nodeId}`].status]
    }
    return status
  }

  /**
   * 节点可视化对比  选中和取消选中
   * @param node 节点
   * @param $event
   */
  public changeNodeComparison(node: any, $event: MouseEvent) {
    $event.stopPropagation()
    const index = this.comparisonNodeIds.indexOf(node.id)
    if (index > -1) {
      this.comparisonNodeIds.splice(index, 1)
      this.comparisonNodes.splice(index, 1)
    } else {
      this.comparisonNodeIds.push(node.id)
      this.comparisonNodes.push(node)
    }
  }

  /**
   * 展示 节点 右键 菜单
   * @param event_
   * @param node 当前节点
   */
  public nodeContextMenu(event_: { preventDefault: () => void }, node: any) {
    // 记录当前编辑的节点
    this.editCurrentNode = node
    this.menuOpenedId = node.id
    event_.preventDefault()
  }

  /**
   * 节点菜单定位
   */
  public nodeMenuPosition(nodePosition: { x: number; y: number }) {
    const runLogBoundingClientRect = (document.querySelector(
      '.run-log'
    ) as HTMLDivElement).getBoundingClientRect()
    return (
      this.containerHeightWidth[0] -
        runLogBoundingClientRect.height -
        nodePosition.y <
      200
    )
  }

  // 节点 菜单相关操作

  /**
   * 节点复制
   */
  public async nodeCopy(nodeId: number, $event: MouseEvent) {
    $event.stopPropagation()
    const parameters = {
      id: nodeId,
    }
    const response = await nodeCopy({ data: parameters })
    if (response.data.code === 100) {
      this.menuOpenedId = null
      // 复制成功更新所有节点
      await this.getAllNodes()
      this.$set(this, 'runStatusMap', this.runStatusMap)
      this.$message.success('节点复制成功！', 0.5)
    }
  }

  /**
   * 参数配置--编辑ETL参数
   * @param node 当前节点
   * @param $event
   */
  public editEtlParams(node: any, $event: MouseEvent) {
    $event.stopPropagation()
    // 最大父节点数量 maxParentNumber === -1 不限制数量
    if (
      (node.data.maxParentNumber > 1 &&
        node.parentId &&
        node.parentId.split(',').length === node.data.maxParentNumber) ||
      node.data.maxParentNumber === -1 ||
      node.data.maxParentNumber === 1
    ) {
      this.editCurrentNode = node
      // 深拷贝整个节点信息（取消编辑问题）
      this.editCurrentNodeDeepCopy = JSON.parse(JSON.stringify(node))
      this.visiblePopup = { name: ETLAlgType[node.data.algType] }
      this.menuOpenedId = null
    } else {
      this.$message.warning('请确认节点父子关系！')
    }
  }

  /**
   * 参数配置--确认编辑ETL参数
   * @param setETlParameters 配置参数
   */
  public confirmEtlParams(setETlParameters: any) {
    const dataJson: any = {}
    Object.keys(setETlParameters).forEach((key: string) => {
      dataJson[key] = setETlParameters[key]
      dataJson.algType = this.editCurrentNode.data.algType
    })
    const parameters = {
      id: this.editCurrentNode.id,
      parentId: this.editCurrentNode.parentId,
      projectId: this.projectId,
      pipelineId: this.pipelineId,
      type: this.editCurrentNode.type,
      data: dataJson,
    }
    this.nodeUpdate(parameters)
  }

  /**
   * 参数配置--编辑算子
   * @param node 当前节点
   * @param $event
   */
  public editOperatorParams(node: any, $event: MouseEvent) {
    $event.stopPropagation()
    this.editCurrentNode = node
    // 深拷贝整个节点信息（取消编辑问题）
    this.editCurrentNodeDeepCopy = JSON.parse(JSON.stringify(node))
    this.visiblePopup = { name: 'set-params' }
    this.menuOpenedId = null
  }

  /**
   * 参数配置--编辑算子(function endConnect)
   * @param setParameters 配置参数
   */
  public confirmOperatorParams(setParameters: any) {
    const dataJson: any = {}
    Object.keys(setParameters).forEach((key: string) => {
      dataJson[key] = setParameters[key]
      dataJson.algType = this.editCurrentNode.data.algType
    })
    const parameters = {
      id: this.editCurrentNode.id,
      projectId: this.projectId,
      pipelineId: this.pipelineId,
      parentId: this.editCurrentNode.parentId,
      type: this.editCurrentNode.type,
      data: dataJson,
    }
    this.nodeUpdate(parameters)
  }

  /**
   * 节点更新接口  连接线  算子配置  ETL配置
   * @param parameters 更新参数
   */
  public async nodeUpdate(parameters: any) {
    const response = await updateNodeToPipeline({ data: parameters })
    if (response.data.code === 100) {
      this.menuOpenedId = null
      // 更新所有节点
      await this.getAllNodes()
      this.cancelBtn()
    }
  }

  /**
   * 预览数据--查看数据
   * @param data 当前数据
   * @param $event
   */
  public async checkDataInfo(data: any, $event: MouseEvent) {
    $event.stopPropagation()
    // 没有运行 不能预览拦截
    // if(this.runStatusMap.length === 0) {
    //   return
    // }
    const response = await infoData({
      data: { datasetId: data.data.dataset_id, projectId: this.projectId },
    })
    // 请求成功弹框显示
    if (response.data.code === 100) {
      this.menuOpenedId = null
      this.currentDataInfo = response.data.result
      this.infoDataVisible = true
    }
  }

  /**
   * 算子节点 数据预览
   * @param node
   * @param $event
   */
  public async nodePreview(node: any, $event: MouseEvent) {
    $event.stopPropagation()
    // 没有运行 不能预览拦截
    // if(this.runStatusMap.length === 0) {
    //   return
    // }
    const response = await nodeDataPreview({
      data: { id: node.id, pipelineId: this.pipelineId },
    })
    // 请求成功弹框显示
    if (response.data.code === 100) {
      this.currentOperatorInfo = response.data.result
      this.infoOperatorVisible = true
      this.menuOpenedId = null
    }
  }

  /**
   * 添加可视化
   * @param node
   * @param $event
   */
  public addVisual(node: any, $event: MouseEvent) {
    $event.stopPropagation()
    // 关闭menu
    this.menuOpenedId = null
    // store 存储当前选择的 节点 确保
    DataViewStore.changeSelectNode(node)
    DataViewStore.setVisualWindowShow(true)
  }

  /**
   * 删除--删除节点
   * @param id 节点id
   * @param $event
   */
  public async deleteNode(id: number, $event: MouseEvent) {
    $event.stopPropagation()
    const response = await deleteNodeById({ data: { id } })
    if (response.data.code === 100) {
      this.$message.success('节点删除成功！', 0.5)
      this.cancelSelectNode()
      // 删除节点更新pipeline所有节点状态
      await this.getAllNodes()
      this.menuOpenedId = null
    } else if (response.data.code === 500) {
      this.$message.warning(response.data.tips)
    }
  }

  /**
   * 禁用--禁用节点
   */
  public forbiddenNode($event: MouseEvent) {
    $event.stopPropagation()
    const parameters = {
      id: this.editCurrentNode.id,
      projectId: this.projectId,
      pipelineId: this.pipelineId,
      data: {
        forbidden: !this.editCurrentNode.data.forbidden,
      },
    }
    this.nodeUpdate(parameters)
  }

  // 节点连接动作线 操作

  /**
   * 开始连接
   * @param event_
   * @param type  开始节点类型  1-算子 2-ETL 3-数据
   * @param nodeId  节点Id
   * @param output
   *
   */
  public startConnect(
    event_: { stopPropagation: () => void; x: number; y: number },
    type: number,
    nodeId: number,
    output: any
  ) {
    this.initVariable()
    event_.stopPropagation()
    // 显示动作线 设置起始位置
    this.connectInfo.show = true
    this.setConnectStartEndPosition(
      [event_.x - this.containerPos[0], event_.y - this.containerPos[1]],
      [event_.x - this.containerPos[0], event_.y - this.containerPos[1]]
    )
    // 记录起始节点的信息
    this.connectInfo.pair[0].type = type
    this.connectInfo.pair[0].nodeId = nodeId
    this.connectInfo.pair[0].output = output
    document.addEventListener('mousemove', this.moveConnect)
    document.addEventListener('mouseup', this.mouseUpConnect)
  }

  /**
   * 设置动作线开始结束位置
   * @param startPos 起始位置
   * @param endPos 结束位置
   */
  public setConnectStartEndPosition(
    startPos: Array<number>,
    endPos: Array<number>
  ) {
    this.$set(this.connectInfo.startEndPos, 0, startPos[0])
    this.$set(this.connectInfo.startEndPos, 1, startPos[1])
    this.$set(this.connectInfo.startEndPos, 2, endPos[0])
    this.$set(this.connectInfo.startEndPos, 3, endPos[1])
  }

  /**
   * 设置动作线结束位置
   * @param event_
   */
  public moveConnect(event_: {
    stopPropagation: () => void
    x: number
    y: number
  }) {
    event_.stopPropagation()
    this.$set(this.connectInfo.startEndPos, 2, event_.x - this.containerPos[0])
    this.$set(this.connectInfo.startEndPos, 3, event_.y - this.containerPos[1])
  }

  /**
   * 鼠标抬起结束连接
   */
  public mouseUpConnect() {
    // 连接完成初始化
    this.connectInfo.show = false
    this.setConnectStartEndPosition([0, 0], [0, 0])
    document.removeEventListener('mousemove', this.moveConnect)
    document.removeEventListener('mouseup', this.mouseUpConnect)
  }

  /**
   *  结束连接
   *  @param type  结束节点类型  1-算子 2-ETL 3-数据
   *  @param nodeId  节点Id
   *  @param node  节点信息
   *  @param algType
   */
  public endConnect(type: number, nodeId: number, node: any, algType: number) {
    if (nodeId === this.connectInfo.pair[0].nodeId) {
      this.$message.error('节点自身不能连接！')
      return
    }
    if (this.connectInfo.pair[0].nodeId === 0) {
      return
    }
    this.editCurrentNode = node
    this.connectInfo.show = false
    this.connectInfo.pair[1].type = type
    this.connectInfo.pair[1].nodeId = nodeId

    // 更新父子关系
    const parameters = {
      id: nodeId,
      projectId: this.projectId,
      pipelineId: this.pipelineId,
      // 当前节点有 parentId 拼接  ',' 隔开
      parentId: node.parentId
        ? `${node.parentId},${this.connectInfo.pair[0].nodeId}`
        : `${this.connectInfo.pair[0].nodeId}`,
      type,
      data: {
        algType,
      },
    }
    // 连线更新节点 建立父子关系
    this.nodeUpdate(parameters)
    // 连接中间信息初始化
    this.connectInfo.startEndPos = [0, 0, 0, 0]
    this.connectInfo.pair = [
      { type: 0, nodeId: 0, output: [] },
      { type: 0, nodeId: 0, output: [] },
    ]
    document.removeEventListener('mousemove', this.moveConnect)
    document.removeEventListener('mouseup', this.mouseUpConnect)
  }

  // 节点连接线相关

  /**
   *  展示 连接线 右键 菜单
   *  @param $event
   *  @param edge  连接线信息
   */
  public connectLineContextmenu($event: MouseEvent, edge: any) {
    $event.preventDefault()
    this.edgeMenuInfo.show = true
    this.edgeMenuInfo.x = $event.x - this.containerPos[0]
    this.edgeMenuInfo.y = $event.y - this.containerPos[1]
    this.edgeMenuInfo.targetInfo.connect = edge.info.connect
  }

  public hoverLine(edge: any) {
    this.startNodeId = edge.startNodeId
    this.endNodeId = edge.endNodeId
  }

  public leaveLine() {
    this.startNodeId = null
    this.endNodeId = null
  }

  public selectLine($event: MouseEvent, edge: any) {
    $event.preventDefault()
    this.startNodeId = edge.startNodeId
    this.endNodeId = edge.endNodeId
  }

  /**
   * 删除连接线
   */
  public async deleteConnectLine() {
    const findNode: any = this.findElementFromEdgeNode(
      this.edgeMenuInfo.targetInfo.connect[1]
    )
    const response = await deleteLineById({ data: { id: findNode.id } })
    if (response.data.code === 100) {
      this.$message.success('连接线删除成功！', 0.5)
      // 删除节点更新pipeline所有节点状态
      await this.getAllNodes()
      // 隐藏menu
      this.hideAllMenu()
    }
  }

  /**
   * 获取所有有 parentId 的节点， 生成节点对
   * parentId: string '23,45,56' 多个父节点 ',' 隔开
   */
  public edgesList() {
    const edgesList: Array<any> = []
    if (this.operatorNodes.length > 0) {
      this.operatorNodes.forEach((node) => {
        // 存在parentId
        if (node.parentId) {
          const parentIdArray = node.parentId.split(',')
          if (parentIdArray.length > 0) {
            // 多个父节点  生成多个父子关系
            parentIdArray.forEach((parentId: string, index: number) => {
              edgesList.push([
                { type: node.data.parentType[index], nodeId: Number(parentId) },
                { type: node.type, nodeId: node.id },
              ])
            })
          }
        }
      })
    }
    if (this.etlNodes.length > 0) {
      this.etlNodes.forEach((node) => {
        // 存在parentId
        if (node.parentId) {
          const parentIdArray = node.parentId.split(',')
          if (parentIdArray.length > 0) {
            // 多个父节点  生成多个父子关系
            parentIdArray.forEach((parentId: string, index: number) => {
              edgesList.push([
                { type: node.data.parentType[index], nodeId: Number(parentId) },
                { type: node.type, nodeId: node.id },
              ])
            })
          }
        }
      })
    }
    if (this.userDefinedNodes.length > 0) {
      this.userDefinedNodes.forEach((node) => {
        // 存在parentId
        if (node.parentId) {
          const parentIdArray = node.parentId.split(',')
          if (parentIdArray.length > 0) {
            // 多个父节点  生成多个父子关系
            parentIdArray.forEach((parentId: string, index: number) => {
              edgesList.push([
                { type: node.data.parentType[index], nodeId: Number(parentId) },
                { type: node.type, nodeId: node.id },
              ])
            })
          }
        }
      })
    }
    return edgesList
  }

  /**
   * 根据节点对连接线信息渲染列表
   */
  public getEdgeInfo() {
    const edgeInfo: Array<any> = []
    const edges = this.edgesList()
    // 遍历节点对
    edges.forEach((nodePair) => {
      const startElement: any = this.findElementFromEdgeNode(nodePair[0])
      const endElement: any = this.findElementFromEdgeNode(nodePair[1])
      if (
        Object.keys(startElement).length > 0 &&
        Object.keys(endElement).length > 0
      ) {
        const startCenter = [
          startElement.data.position.x + 0.5 * this.nodeWidth,
          startElement.data.position.y + 0.5 * this.nodeHeight,
        ]
        const endCenter = [
          endElement.data.position.x + 0.5 * this.nodeWidth,
          endElement.data.position.y + 0.5 * this.nodeHeight,
        ]
        const x = Math.min(startCenter[0], endCenter[0])
        const y = Math.min(startCenter[1], endCenter[1])
        edgeInfo.push({
          startNodeId: startElement.id, // 开始节点id
          endNodeId: endElement.id, // 结束节点id
          startEndPos: [
            startCenter[0] - x,
            startCenter[1] - y,
            endCenter[0] - x,
            endCenter[1] - y,
          ],
          x,
          y,
          info: {
            connect: nodePair,
            rootStyle: { height: this.nodeHeight, width: this.nodeWidth },
            childStyle: { height: this.nodeHeight, width: this.nodeWidth },
          },
        })
      }
    })
    return edgeInfo
  }

  /**
   * 根据节点类型和Id查找节点
   *  @param nodeInfo  type: 1-算子  2-etl  3-数据  (4-结果)
   */
  public findElementFromEdgeNode(nodeInfo: { type: number; nodeId: number }) {
    let node = {}
    if (nodeInfo.type === 3) {
      const index = this.dataNodes.findIndex((item) => {
        return item.id === nodeInfo.nodeId
      })
      if (index >= 0) {
        node = this.dataNodes[index]
      }
    } else if (nodeInfo.type === 1) {
      const index = this.operatorNodes.findIndex(
        (item) => item.id === nodeInfo.nodeId
      )
      if (index >= 0) {
        node = this.operatorNodes[index]
      }
    } else if (nodeInfo.type === 2) {
      const index = this.etlNodes.findIndex(
        (item) => item.id === nodeInfo.nodeId
      )
      if (index >= 0) {
        node = this.etlNodes[index]
      }
    } else if (nodeInfo.type === 4) {
      const index = this.userDefinedNodes.findIndex(
        (item) => item.id === nodeInfo.nodeId
      )
      if (index >= 0) {
        node = this.userDefinedNodes[index]
      }
    }
    return node
  }

  // 节点拖拽 相关

  /**
   * 节点拖拽改变位置
   * @param event_
   * @param node  当前节点信息
   */
  public startDragging(event_: MouseEvent, node: any) {
    this.firstTime = new Date().getTime()
    this.mouseLastPosition = { x: event_.x, y: event_.y }
    this.editCurrentNode = node
    document.addEventListener('mousemove', this.dragMouseMoveListener)
    document.addEventListener('mouseup', this.dragMouseUpListener)
  }
  /**
   * 拖拽结束 节点拖拽鼠标抬起监听  update node position
   */
  public async dragMouseUpListener() {
    this.dragging = false
    document.removeEventListener('mousemove', this.dragMouseMoveListener)
    document.removeEventListener('mouseup', this.dragMouseUpListener)
    const parameters = {
      id: this.editCurrentNode.id,
      projectId: this.projectId,
      pipelineId: this.pipelineId,
      data: {
        position: {
          x: this.editCurrentNode.data.position.x,
          y: this.editCurrentNode.data.position.y,
        },
      },
    }
    const lastTime = new Date().getTime()
    // 点击 和拖拽冲突问题
    if (lastTime - this.firstTime > 300) {
      this.dragging = true
      await updateNodeToPipeline({ data: parameters })
    }
  }
  /**
   * 节点拖拽鼠移动监听
   * @param $event
   */
  public dragMouseMoveListener($event: MouseEvent) {
    const dx = $event.x - this.mouseLastPosition.x
    const dy = $event.y - this.mouseLastPosition.y
    this.editCurrentNode.data.position.x += dx
    this.editCurrentNode.data.position.y += dy
    // x不能出界
    this.editCurrentNode.data.position.x = Math.max(
      0,
      this.editCurrentNode.data.position.x
    )
    this.editCurrentNode.data.position.x = Math.min(
      this.containerHeightWidth[1] - this.nodeWidth,
      this.editCurrentNode.data.position.x
    )
    // y不能出界
    this.editCurrentNode.data.position.y = Math.max(
      0,
      this.editCurrentNode.data.position.y
    )
    this.editCurrentNode.data.position.y = Math.min(
      this.containerHeightWidth[0] - this.nodeHeight,
      this.editCurrentNode.data.position.y
    )
    this.mouseLastPosition = { x: $event.x, y: $event.y }
  }

  /**
   * 选择节点
   * @param $event
   * @param node
   */
  public selectNode($event: MouseEvent, node: any) {
    $event.stopPropagation()
    // 点击和拖拽的冲突
    if (!this.dragging) {
      if (this.selectedPipelineNode === node) {
        this.selectedPipelineNode = null
        DataViewStore.changeSelectNode(null)
      } else {
        this.selectedPipelineNode = node
        // store 存储当前选择的 节点
        DataViewStore.changeSelectNode(node)
      }
    }
  }

  // 连接线  height
  public containerHeight(edge: any) {
    return Math.abs(edge.startEndPos[3] - edge.startEndPos[1]) + 2 * this.pad
  }
  // 连接线 width
  public containerWidth(edge: any) {
    return Math.abs(edge.startEndPos[2] - edge.startEndPos[0]) + 2 * this.pad
  }
  // 连接线
  public getPathD(edge: any) {
    const startPos = [
      edge.startEndPos[0] + this.pad,
      edge.startEndPos[1] + this.pad,
    ]
    const endPos = [
      edge.startEndPos[2] + this.pad,
      edge.startEndPos[3] + this.pad,
    ]
    if (edge.startEndPos[1] < edge.startEndPos[3]) {
      startPos[1] += 0.5 * edge.info.rootStyle.height
      endPos[1] -= 0.5 * edge.info.childStyle.height
    } else if (edge.startEndPos[1] > edge.startEndPos[3]) {
      startPos[1] -= 0.5 * edge.info.rootStyle.height
      endPos[1] += 0.5 * edge.info.childStyle.height
    }
    const controlPt0 = [startPos[0], endPos[1]]
    const controlPt1 = [endPos[0], startPos[1]]
    return `M${startPos[0]},${startPos[1]} C${controlPt0[0]},${controlPt0[1]},${controlPt1[0]},${controlPt1[1]},${endPos[0]},${endPos[1]}`
  }

  public lineStatus(endNodeId: any) {
    let status = ''
    if (
      this.runStatusMap != null &&
      Object.keys(this.runStatusMap).includes(`${endNodeId}`)
    ) {
      if (this.runStatusMap[`${endNodeId}`].status === 'RUNNING') {
        status = '5,5'
      }
    }
    return status
  }

  //  popup close
  public cancelBtn() {
    this.visiblePopup = { name: '' }
  }
}
</script>

<style lang="less" scoped>
.graph-cont {
  background-color: #f0f2f5;
  box-sizing: border-box;
  height: 100%;
  padding: 30px;
  position: relative;
  width: 100%;

  .graph {
    border: 1px dashed transparent;
    box-sizing: border-box;
    height: 100%;
    position: relative;
    width: 100%;

    &.dragging-to {
      border: 1px dashed #ddd;
    }

    .node.selected {
      border: 1px solid #6570ff;
    }
    /* 连接动作线 */
    .connect-line-div {
      height: 100%;
      pointer-events: none;
      position: absolute;
      width: 100%;
      z-index: 32;

      .connect-line-svg {
        height: 100%;
        width: 100%;

        .connect-line {
          fill: none;
          stroke: #000;
          stroke-dasharray: 4px;
          stroke-width: 2px;
        }
      }
    }

    /* 连接线 */
    .flow-edge-cell {
      pointer-events: none;
      position: absolute;
      z-index: 29;

      .edge-container {
        pointer-events: none;
        position: relative;
        user-select: none;
        z-index: 10;

        .edge-svg {
          pointer-events: none;

          .line-cls {
            fill: none;
            stroke: #9b9da8;
          }

          path {
            cursor: pointer;
            fill: none;
            pointer-events: all;
            stroke: rgba(0, 0, 0, 0);
            stroke-width: 20px;
          }
        }
      }
    }
  }

  .pipeline-loading {
    left: 50%;
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
  }
}
/* 节点 */
.node {
  align-items: center;
  background-color: #fff;
  border: 1px solid #dbdfea;
  border-radius: 16px;
  box-sizing: border-box;
  cursor: pointer;
  display: flex;
  flex-direction: row;
  font-size: 0.9rem;
  justify-content: space-between;
  padding: 0 2px;
  position: absolute;
  text-align: center;
  user-select: none;
  z-index: 30;

  .node-no-forbidden {
    height: 0;
    left: 0;
    position: absolute;
    top: 0;
    width: 0;
  }
  /* 节点禁用灰度 */
  .node-forbidden {
    background-color: rgba(255, 255, 255, 0.3);
    border-radius: 16px;
    height: 100%;
    width: 100%;
  }

  /* 节点图标 */
  .type-icon {
    align-items: center;
    background-color: rgba(154, 162, 255, 1);
    border-radius: 50%;
    display: flex;
    font-size: 18px;
    height: 26px;
    justify-content: center;
    line-height: 26px;
    width: 26px;
  }
  /* 节点name */
  .name {
    line-height: 30px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    width: 93px;

    .node-name-tooltip {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      width: 100%;
    }
  }

  .menu-hover-ellipsis {
    align-items: center;
    display: flex;
    height: 30px;
    margin-right: 5px;
    width: 16px;
    z-index: 1;

    .type-icon-color {
      display: none;
    }
  }

  /* 节点status */
  .status-icon {
    font-size: 16px;
    height: 26px;
    line-height: 26px;
    position: relative;
    width: 26px;
  }
  /* 对比图标 */
  .comparison-select-status {
    font-size: 26px;
    height: 26px;
    line-height: 26px;
    width: 26px;
  }

  &:hover {
    .connect-cell {
      display: block;
    }

    .menu-hover-ellipsis {
      .type-icon-color {
        display: block;
      }
    }
  }
  /* 节点连接点 */
  .connect-cell {
    background-color: #fff;
    border: 1px solid #777;
    border-radius: 50%;
    cursor: pointer;
    display: none;
    height: 12px;
    left: 88px;
    position: absolute;
    width: 12px;
    z-index: 3;

    &:hover {
      background-color: #aaa;
      border: 1px solid transparent;
    }
  }
  /* 节点上连接点 */
  .connect-cell-up {
    top: -6px;
  }
  /* 节点下连接点 */
  .connect-cell-down {
    bottom: -6px;
  }
}

/* 数据  算子  ETL 节点 menu */
.menu {
  background-color: #fff;
  border: 1px solid #ddd;
  border-radius: 2px;
  box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.18);
  font-size: 0.9rem;
  left: 9px;
  list-style: none;
  margin: 0;
  padding: 4px 0;
  position: absolute;
  width: 160px;
  z-index: 3000;

  .item {
    cursor: pointer;
    height: 22px;
    line-height: 22px;
    margin: 1px 0;
    padding: 0 5px;
  }

  .divider {
    border-bottom: 1px solid #ddd;
    height: 1px;
    margin: 7px 0;
  }

  /* 不可用菜单功能 */
  .not-useable {
    background-color: rgba(8, 8, 8, 0.1);
    color: #999;
  }

  .item:hover {
    background-color: #eff0fe;
  }
}

.menu-top {
  bottom: calc(100% + 9px);

  &::before {
    background-color: transparent;
    bottom: -9px;
    content: '';
    height: 9px;
    position: absolute;
    right: 0;
    width: 160px;
  }
  /*top: none*/
}

.menu-bottom {
  top: calc(100% + 9px);

  &::before {
    background-color: transparent;
    content: '';
    height: 9px;
    position: absolute;
    right: 0;
    top: -9px;
    width: 160px;
  }
  /*top: none*/
}

/* 连接线编辑菜单 */
.menu-container {
  .a-menu-container {
    background-color: rgba(255, 255, 255, 1);
    border-radius: 4px;
    box-shadow: 0 1px 4px 0 rgba(178, 179, 196, 1);
    width: 120px;

    .a-menu-cell {
      border-radius: 4px 4px 0 0;
      height: 36px;
      line-height: 36px;
      margin: 0;
      width: 100%;

      &:hover {
        background: rgba(239, 240, 254, 1);
      }
    }
  }
}

.type-icon-color {
  color: #fff;
}

/*
pop-up
*/
.overlay {
  background-color: rgba(0, 0, 0, 0.2);
  height: 100vh;
  left: 0;
  position: fixed;
  top: 0;
  width: 100vw;
  z-index: 1000;
}

.window {
  background: #fff;
  border: 1px solid #e6e6e6;
  border-radius: 2px;
  height: 250px;
  left: 50%;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 300px;
  z-index: 99;
}
</style>
